/*
 [The "BSD license"]
 Copyright (c) 2011-2024  闲大赋 (李家智)
 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions
 are met:
 1. Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.
 2. Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in the
     documentation and/or other materials provided with the distribution.
 3. The name of the author may not be used to endorse or promote products
     derived from this software without specific prior written permission.

 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.beetl.core;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.antlr.v4.runtime.ParserRuleContext;
import org.beetl.core.statement.*;

/**
 * 结合AntlrProgramBuilder 将模板生成Program，该类负责记录变量定义的位置和出现的位置
 *
 * @author xiandafu
 */
public class ProgramBuilderContext {
	// 最顶层的变量,非全局变量
	public BlockEnvContext root = newBlockEnvContext();
	// 当前block
	public BlockEnvContext current = root;

	// 节点运算辅助对象
	protected List<Object> listNodeEval = new LinkedList<Object>();
	// 全局变量名以及描述
	protected Map<String, VarDescription> globalVar = new HashMap<String, VarDescription>();

	// 为所有变量分配的空间长度
	protected int varIndexSize = 0;
	// 全局变量在空间中的位置
	public Map<String, Integer> globalIndexMap = new HashMap<String, Integer>();

	protected boolean isSafeOutput = false;

	/**
	 * 顶级变量在空间中的位置
	 */
	public Map<String, Integer> rootIndexMap = new HashMap<String, Integer>();

	/**
	 * 进入一个scope
	 */
	public void enterBlock(ParserRuleContext ruleContext) {
		BlockEnvContext blockVar =newBlockEnvContext();
		blockVar.setParent(current);
		current = blockVar;
	}

	public void exitBlock(Statement statement) {
		current.setBlockRef(statement);
		current = current.parent;
	}

	public void addVarAndPostion(ASTNode first) {
		this.addVar(first);
		this.setVarPosition(first.token.text, first);
	}

	/**
	 * 动态添加一个顶级变量
	 */
	public boolean addRootVarAdnPosition(ASTNode first) {
		String varName = first.token.text;
		//需要查找是否已经被定义过了，不能定义
		if (searchVar(root, varName) != null) {
			return true;
		}

		VarDescription varDesc = newVarDescription();
		varDesc.setVarName(varName);
		varDesc.where.add(first);
		root.getVars().put(varName, varDesc);
		return false;
	}

	/**
	 * 自上向下查找
	 */
	public ASTNode searchVar(BlockEnvContext ctx, String name) {
		if (ctx.getVarDescrption(name) != null) {
			return ctx.getVarDescrption(name).where.get(0);
		}
		for (BlockEnvContext child : ctx.blockList) {
			ASTNode node = searchVar(child, name);
			if (node != null) {
				return node;
			}
		}
		return null;
	}

	/**
	 * 在当前context定义变量
	 */
	public VarDescription addVar(ASTNode varNode) {
		VarDescription varDesc = newVarDescription();
		varDesc.setVarName(varNode.token.text);
		this.current.getVars().put(varDesc.getVarName(), varDesc);
		return varDesc;
	}

	public ASTNode contain(String varName) {
		VarDescription varDesc = this.current.getVars().get(varName);
		if (varDesc == null) {
			return null;
		} else {
			return varDesc.where.get(0);
		}
	}

	/**
	 * 变量属性，展示没用上，本来想用在ide属性提示,但ide插件门槛太高了，搞不定
	 */
	public void setVarAttr(String varName, String attrName) {
		VarDescription varDesc = findVar(varName);
		varDesc.attrList.add(attrName);
	}

	public void setVarPosition(String varName, ASTNode where) {
		VarDescription varDesc = findVar(varName);
		varDesc.where.add(where);
	}

	protected GrammarToken hasDefined(String varName) {
		BlockEnvContext scope = current;
		while (scope != null) {
			VarDescription varDesc = scope.getVarDescrption(varName);
			if (varDesc != null) {
				return varDesc.where.get(0).token;
			} else {
				scope = scope.parent;
			}
		}
		return null;
	}

	protected VarDescription findVar(String varName) {
		BlockEnvContext scope = current;
		while (scope != null) {
			VarDescription varDesc = scope.getVarDescrption(varName);
			if (varDesc != null) {
				return varDesc;
			} else {
				scope = scope.parent;
			}
		}

		// 未发现，是模板全局变量
		VarDescription desc = globalVar.get(varName);
		if (desc == null) {
			desc = newVarDescription();
			desc.setVarName(varName);
			globalVar.put(varName, desc);
		}

		return desc;

	}

	public int setNodeEvalObject(Object o) {
		listNodeEval.add(o);
		return listNodeEval.size() - 1;
	}

	public void anzlyszeGlobal() {
		int index = 0;
		for (Entry<String, VarDescription> entry : globalVar.entrySet()) {
			globalIndexMap.put(entry.getKey(), index);
//			//TODO,为啥这里为空
//			entry.getValue().setVarName(entry.getKey());
			varIndexPoint(current,entry.getValue(),index);
			VarDescription vd = entry.getValue();
			for (ASTNode node : vd.where) {
				((IVarIndex) node).setVarIndex(index);

			}
			index++;
		}
	}

	protected void varIndexPoint(BlockEnvContext blockEnvContext, VarDescription vd, int index){

	}

	public void anzlyszeLocal() {
		anzlysze(this.root, this.globalVar.size(), true);
	}

	protected void anzlysze(BlockEnvContext block, int nextIndex, boolean isRoot) {

		for (Entry<String, VarDescription> entry : block.vars.entrySet()) {
			VarDescription vd = entry.getValue();
			for (ASTNode node : vd.where) {
				((IVarIndex) node).setVarIndex(nextIndex);
				varIndexPoint(block,vd,nextIndex);
				if (isRoot) {
					this.rootIndexMap.put(vd.getVarName(), nextIndex);
				}
			}
			nextIndex++;
		}
		varIndexSize = Math.max(varIndexSize, nextIndex);

		for (BlockEnvContext subBlock : block.blockList) {
			anzlysze(subBlock, nextIndex, false);
			int inc = subBlock.vars.size();
			varIndexSize = Math.max(varIndexSize, nextIndex + inc);
		}

	}

	public BlockEnvContext newBlockEnvContext(){
		return new BlockEnvContext();
	}
	public VarDescription newVarDescription(){
		return new VarDescription();
	}

}

